#include "OgreRefAppGoalKeeperOwnedStates.h"
#include "OgreRefAppGoalKeeper.h"
#include "OgreRefAppBall.h"
#include "OgreRefAppSoccerPitch.h"


//#define __DEBUGON__

namespace OgreRefApp
{

//--------------------------- GlobalKeeperState -------------------------------
//-----------------------------------------------------------------------------

    GlobalKeeperState* GlobalKeeperState::Instance()
    {
        static GlobalKeeperState instance;

        return &instance;
    }


    bool GlobalKeeperState::OnMessage(GoalKeeper* keeper, OgreRefApp::ApplicationObject::CollisionInfo& telegram)
    {
        switch (telegram.Msg)
        {
        case Msg_GoHome:
        {
            keeper->SetDefaultHomeRegion();

            keeper->GetFSM()->ChangeState(ReturnHome::Instance());
        }

        break;

        case Msg_ReceiveBall:
        {
            keeper->GetFSM()->ChangeState(InterceptBall::Instance());
        }

        break;

        }//end switch

        return false;
    }


//--------------------------- TendGoal -----------------------------------
//
//  This is the main state for the goalkeeper. When in this state he will
//  move left to right across the goalmouth using the 'interpose' steering
//  behavior to put himself between the ball and the back of the net.
//
//  If the ball comes within the 'goalkeeper range' he moves out of the
//  goalmouth to attempt to intercept it. (see next state)
//------------------------------------------------------------------------

    TendGoal* TendGoal::Instance()
    {
        static TendGoal instance;

        return &instance;
    }


    void TendGoal::Enter(GoalKeeper* keeper)
    {
        #ifdef __DEBUGON__
        std::cout << "START: TendGoal::Enter" << std::endl;
        #endif

        //turn interpose on
        keeper->Steering()->InterposeOn(GoalKeeperTendingDistance);

        #ifdef __DEBUGON__
        std::cout << "END: keeper->Steering()->InterposeOn()" << std::endl;
        #endif
        //interpose will position the agent between the ball position and a target
        //position situated along the goal mouth. This call sets the target
        keeper->Steering()->SetTarget(keeper->GetRearInterposeTarget());

        #ifdef __DEBUGON__
        std::cout << "END: TendGoal::Enter" << std::endl;
        #endif

    }

    void TendGoal::Execute(GoalKeeper* keeper)
    {

        #ifdef __DEBUGON__
        std::cout << "TendGoal::Execute" << std::endl;
        #endif
        //the rear interpose target will change as the ball's position changes
        //so it must be updated each update-step
        keeper->Steering()->SetTarget(keeper->GetRearInterposeTarget());

        //if the ball comes in range the keeper traps it and then changes state
        //to put the ball back in play
        if (keeper->BallWithinKeeperRange())
        {
            keeper->getBall()->Trap();

            keeper->Pitch()->setGoalKeeperHasBall(true);

            keeper->GetFSM()->ChangeState(PutBallBackInPlay::Instance());

            return;
        }

        //if ball is within a predefined distance, the keeper moves out from
        //position to try and intercept it.
        if (keeper->BallWithinRangeForIntercept() && !keeper->Team()->InControl())
        {
            keeper->GetFSM()->ChangeState(InterceptBall::Instance());
        }

        //if the keeper has ventured too far away from the goal-line and there
        //is no threat from the opponents he should move back towards it
        if (keeper->TooFarFromGoalMouth() && keeper->Team()->InControl())
        {
            keeper->GetFSM()->ChangeState(ReturnHome::Instance());

            return;
        }
    }


    void TendGoal::Exit(GoalKeeper* keeper)
    {
        keeper->Steering()->InterposeOff();
    }


//------------------------- ReturnHome: ----------------------------------
//
//  In this state the goalkeeper simply returns back to the center of
//  the goal region before changing state back to TendGoal
//------------------------------------------------------------------------

    ReturnHome* ReturnHome::Instance()
    {
        static ReturnHome instance;

        return &instance;
    }


    void ReturnHome::Enter(GoalKeeper* keeper)
    {
        keeper->Steering()->ArriveOn();
    }

    void ReturnHome::Execute(GoalKeeper* keeper)
    {
        #ifdef __DEBUGON__
        std::cout << "ReturnHome::Execute" << std::endl;
        #endif
        keeper->Steering()->SetTarget(Ogre::Vector3(keeper->HomeRegion()->Center().x, keeper->HomeRegion()->Center().y, PlayerHeight/2));

        //if close enough to home or the opponents get control over the ball,
        //change state to tend goal
        if (keeper->InHomeRegion() || !keeper->Team()->InControl())
        {
            keeper->GetFSM()->ChangeState(TendGoal::Instance());
        }
    }

    void ReturnHome::Exit(GoalKeeper* keeper)
    {
        keeper->Steering()->ArriveOff();
    }



//----------------- InterceptBall ----------------------------------------
//
//  In this state the GP will attempt to intercept the ball using the
//  pursuit steering behavior, but he only does so so long as he remains
//  within his home region.
//------------------------------------------------------------------------

    InterceptBall* InterceptBall::Instance()
    {
        static InterceptBall instance;

        return &instance;
    }


    void InterceptBall::Enter(GoalKeeper* keeper)
    {
        keeper->Steering()->PursuitOn();

        #ifdef __DEBUGON__
        std::cout << "InterceptBall::Enter" << std::endl;
        #endif
    }

    void InterceptBall::Execute(GoalKeeper* keeper)
    {
        #ifdef __DEBUGON__
        std::cout << "InterceptBall::Execute" << std::endl;
        #endif
        //if the goalkeeper moves to far away from the goal he should return to his
        //home region UNLESS he is the closest player to the ball, in which case,
        //he should keep trying to intercept it.
        if (keeper->TooFarFromGoalMouth() && !keeper->isClosestPlayerOnPitchToBall())
        {
            keeper->GetFSM()->ChangeState(ReturnHome::Instance());

            return;
        }

        //if the ball becomes in range of the goalkeeper's hands he traps the
        //ball and puts it back in play
        if (keeper->BallWithinKeeperRange())
        {
            keeper->getBall()->Trap();

            keeper->Pitch()->setGoalKeeperHasBall(true);

            keeper->GetFSM()->ChangeState(PutBallBackInPlay::Instance());

            return;
        }
    }

    void InterceptBall::Exit(GoalKeeper* keeper)
    {
        keeper->Steering()->PursuitOff();
    }



//--------------------------- PutBallBackInPlay --------------------------
//
//------------------------------------------------------------------------

    PutBallBackInPlay* PutBallBackInPlay::Instance()
    {
        static PutBallBackInPlay instance;

        return &instance;
    }

    void PutBallBackInPlay::Enter(GoalKeeper* keeper)
    {
        #ifdef __DEBUGON__
        std::cout << "PutBallBackInPlay::Enter" << std::endl;
        #endif
        //let the team know that the keeper is in control
        keeper->Team()->SetControllingPlayer(keeper);

        #ifdef __DEBUGON__
        std::cout << "goleiro 1" << std::endl;
        #endif

        //send all the players home
        keeper->Team()->Opponents()->ReturnAllFieldPlayersToHome();

        #ifdef __DEBUGON__
        std::cout << "goleiro 2" << std::endl;
        #endif

        keeper->Team()->ReturnAllFieldPlayersToHome();

        #ifdef __DEBUGON__
        std::cout << "goleiro 3" << std::endl;
        #endif
    }


    void PutBallBackInPlay::Execute(GoalKeeper* keeper)
    {
        PlayerBase*  receiver = NULL;
        Ogre::Vector3     BallTarget;

        #ifdef __DEBUGON__
        std::cout << "PutBallBackInPlay::Execute 1" << std::endl;
        #endif

        #ifdef __DEBUGON__
        std::cout << "goleiro 4" << std::endl;
        #endif

        //test if there are players further forward on the field we might
        //be able to pass to. If so, make a pass.
        if (keeper->Team()->FindPass(keeper,
                                     receiver,
                                     BallTarget,
                                     MaxPassingForce,
                                     GoalkeeperMinPassDistance))
        {
            #ifdef __DEBUGON__
            std::cout << "goleiro 5" << std::endl;
            #endif
            //make the pass
            keeper->getBall()->Kick(keeper->Team()->Receiver()->getPosition() , 400);
            //goalkeeper no longer has ball
            keeper->Pitch()->setGoalKeeperHasBall(false);

            ApplicationObject::CollisionInfo collInfo;
            collInfo.Msg = Msg_ReceiveBall;
            collInfo.sender = keeper;
            collInfo.position = keeper->getBall()->getPosition();

            keeper->Team()->Receiver()->handleMessage( collInfo );


            //go back to tending the goal
            keeper->GetFSM()->ChangeState(TendGoal::Instance());

            return;
        }



        keeper->setLinearVelocity(Ogre::Vector3(0,0,0));
    }
}
